package cn.xerllent.system.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.validation.ValidationUtil;
import cn.hutool.json.JSONObject;
import cn.xerllent.common.exception.BadRequestException;
import cn.xerllent.common.service.impl.BaseServiceImpl;
import cn.xerllent.common.utils.FileUtil;
import cn.xerllent.system.entity.Log;
import cn.xerllent.system.service.LogService;
import cn.xerllent.system.service.dto.LogFilter;
import cn.xerllent.system.service.mapper.LogMapper;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageInfo;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 */
@Service
//@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class LogServiceImpl extends BaseServiceImpl<LogMapper, Log> implements LogService {

    @Override
    public Object findAllByPageable(String nickname, Pageable pageable) {
    	pageHelper(pageable);
        PageInfo<Log> page = new PageInfo<>(baseMapper.findAllByPageable(nickname));
        Map<String, Object> map = new LinkedHashMap<>(2);
        map.put("list", page.getList());
        map.put("total", page.getTotal());
        return map;
    }


    @Override
    public Object queryAll(LogFilter filter, Pageable pageable) {
    	pageHelper(pageable);
        PageInfo<Log> page = new PageInfo<>(queryAll(filter));
        Map<String, Object> map = new LinkedHashMap<>(2);
        map.put("list", page.getList());
        map.put("total", page.getTotal());
        return map;
    }

    @Override
    public List<Log> queryAll(LogFilter filter) {
    	QueryWrapper<Log> querywrapper = new QueryWrapper<>();
    	if(StringUtils.isNotBlank(filter.getBlurry())) {//username,description,address,requestIp,method,params
    		querywrapper.and(
    				w->w.like("username", filter.getBlurry()).or()
    				.like("description", filter.getBlurry()).or()
    				.like("address", filter.getBlurry()).or()
    				.like("requestip", filter.getBlurry()).or()
    				.like("method", filter.getBlurry()).or()
    				.like("params", filter.getBlurry())
    				);
    	}
		if(filter.getType()!=null) {
			querywrapper.eq("type", filter.getType());
		}
		if(StringUtils.isNotBlank(filter.getLogtype())) {
			querywrapper.eq("logtype", filter.getLogtype());
		}
		List<LocalDateTime>  times = filter.getCreatetime();
		if(times!=null && times.size()>0) {
			LocalDateTime t1=times.get(0).withHour(0).withMinute(0).withSecond(0);
			querywrapper.gt("createtime", t1);
			if(times.size()>1) {
				LocalDateTime t2=times.get(1).plusSeconds(86400).withHour(0).withMinute(0).withSecond(0);
				querywrapper.lt("createtime", t2);
			}
		}
		if(StringUtils.isNotBlank(filter.getUsername())) {
			querywrapper.eq("username", filter.getUsername());
		}
    	
        return baseMapper.selectList(querywrapper);
    }

    @Override
    public Object queryAllByUser(LogFilter filter, Pageable pageable) {
    	pageHelper(pageable);
        PageInfo<Log> page = new PageInfo<>(queryAll(filter));
        Map<String, Object> map = new LinkedHashMap<>(2);
        map.put("list", page.getList());
        map.put("total", page.getTotal());
        return map;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(String username, String ip, ProceedingJoinPoint joinPoint,Log log, Long uid) {

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        cn.xerllent.common.annotation.Log aopLog = method.getAnnotation(cn.xerllent.common.annotation.Log.class);

        // 方法路径
        String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";

        StringBuilder params = new StringBuilder("{");
        //参数值
        Object[] argValues = joinPoint.getArgs();
        //参数名称
        String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        if (argValues != null) {
            for (int i = 0; i < argValues.length; i++) {
                params.append(" ").append(argNames[i]).append(": ").append(argValues[i]);
            }
        }
        // 描述
        if (log != null) {
            log.setDescription(aopLog.value());
        }
        //类型 0-后台 1-前台
        log.setType(aopLog.type());
        if (uid != null) {
            log.setUid(uid);
        }
        assert log != null;
        log.setRequestip(ip);

        String loginPath = "login";
        if (loginPath.equals(signature.getName())) {
            try {
                assert argValues != null;
                username = new JSONObject(argValues[0]).get("username").toString();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        log.setAddress(cn.xerllent.common.utils.StringUtils.getCityInfo(log.getRequestip()));
        log.setMethod(methodName);
        log.setUsername(username);
        log.setParams(params.toString() + " }");
        this.save(log);
    }

    //@cn.xerllent.common.annotation.Log(value="TestJob2任务执行",type=10)  日志记录示例
    @Override
    public Object findByErrDetail(Long id) {
        Log log = this.getById(id);
        if (ObjectUtil.isNull(log)) {
            throw new BadRequestException("没找到日志");
        }
        byte[] details = log.getException();
        return new String(ObjectUtil.isNotNull(details) ? details : "".getBytes());
    }

    @Override
    public void download(List<Log> logs, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (Log log : logs) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("用户名", log.getUsername());
            map.put("IP", log.getRequestip());
            map.put("IP来源", log.getAddress());
            map.put("描述", log.getDescription());
            map.put("浏览器", log.getBrowser());
            map.put("请求耗时/毫秒", log.getTime());
            map.put("异常详情", new String(ObjectUtil.isNotNull(log.getException()) ? log.getException() : "".getBytes()));
            map.put("创建日期", log.getCreatetime().toString());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delAllByError() {
    	baseMapper.deleteByLogType("ERROR");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delAllByInfo() {
    	baseMapper.deleteByLogType("INFO");
    }
    
    //找到用户最后第2条登录日志
    @Override
    public Log findLastLogInUserLog(String username) {
    	return baseMapper.findLastLogInUserLog(username);
    }
    
	/**
	 * 清除对应Mapper的二级缓存，采用无害删除空数据模拟清空,因为二级缓存更新机制是本表数据更新即可清空<br>
	 * 提供本方法供联表更新查询的实时更新
	 * */
	@Override
	public void clearCache() {
		this.baseMapper.deleteById(0);
	}
}
